package com.cherry.example.patterns.single;

/*
单例设计模式：保证一个类在内存中只有一个对象。

设计模式：  解决一类问题的固定步骤。 


Runtime 运行环境类。 一个RunTime对象则代表 了一个运行环境。 一个java应用程序有且只有一个运行环境。


饿汉单例设计模式步骤：
	1. 私有化构造函数。
	2. 声明静态本类的引用类型变量并且创建本类的对象。
	3. 提供公共静态的方法获取本类的对象。

懒汉单例设计模式： 
	1. 私有化构造方法。
	2. 声明本类静态引用类型变量，但是先不用创建对象。
	3. 提供公共静态的方法获取本类的对象，获取之前先判断是否已经创建了本类的对象， 如果还没有创建
	那么就先创建然后在返回，如果已经创建了直接返回即可。

推荐使用： 饿汉设计模式。因为懒汉单例设计模式有线程安全问题存在。


*/

import java.util.HashMap;
import java.util.Map;

class Single {

    //声明本类的静态引用类型变量并且创建本类的对象。
    private static Single s = new Single();


    //私有化构造函数
    private Single() {
    }


    //提供公共静态的方法获取本类的对象
    public static Single getInstance() {
        return s;
    }

}


//懒汉单例设计模式
class Single2 {
    //声明本类静态引用类型变量
    //volatile解决编译器允许处理器的乱序执行
    private volatile static Single2 s;

    //私有化构造方法
    private Single2() {
    }


    /*
     提供公共静态的方法获取本类的对象，获取之前先判断是否已经创建了本类的对象， 如果还没有创建
    那么就先创建然后在返回，如果已经创建了直接返回即可。
     */
    public static Single2 getInstance() {
        if (s == null) {
            synchronized ("锁") {
                if (s == null) {
                    //这里并不是原子操作，1 给Single2实例分配内存 2 调用Single2构造函数，初始化成员字段  3 变量s对象指向分配的内存空间
                    //其中，第2步和第3步可能乱序，多线程会有线程安全问题
                    s = new Single2();
                }
            }
        }
        return s;
    }
}

/**
 * 静态内部类单例
 * 双重检查锁定 DCL 有些情况可能有失效，并且代码不够优雅
 */
class Single3{

    private Single3() {
    }

    public static Single3 getInstance() {
        return SingletonHolder.s;
    }

    private static class SingletonHolder{
        private static final Single3 s = new Single3();
    }

}

/**
 * 枚举实现单例
 * 任何情况都是一个单例，不存在反序列化后可以创建新的实例的问题
 */
enum  Single4{
    INSTANCE;

    public void doSomething(){

    }
}

/**
 * 容器管理多个类型的单例
 */
class SingleManager{

    private static Map<String,Object> objectMap = new HashMap<>();

    private SingleManager() {
    }

    public static void registerService(String key,Object instance){
        if (!objectMap.containsKey(key)){
            objectMap.put(key,instance);
        }
    }

    public static Object getService(String key){
        return objectMap.get(key);
    }

}





public class Demo1 {

    public static void main(String[] args) {
        Single2 s1 = Single2.getInstance();
        Single2 s2 = Single2.getInstance();

        System.out.println("s1与s2是同一个对象吗？" + (s1 == s2));

    }

}
